package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestExpression001(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"#t", "#t"},
		{"#f", "#f"},
		{"(not #t)", "#f"},
		{"(not #f)", "#t"},
		{"(and #t #t)", "#t"},
		{"(and #f #t)", "#f"},
		{"(and #t #t #f)", "#f"},
		{"(or #t #t)", "#t"},
		{"(or #f #t)", "#t"},
		{"(or #t #t #f)", "#t"},
		{"(or #t #t #f)", "#t"},
		{"(or #f #f #t)", "#t"},
		{"(< 1 2)", "#t"},
		{"(< 2 1)", "#f"},
		{"(< 1 2 3)", "#t"},
		{"(< 1 3 2)", "#f"},
		{"(<= 1 2)", "#t"},
		{"(<= 2 2)", "#t"},
		{"(<= 3 2)", "#f"},
		{"(= 2 2)", "#t"},
		{"(= 2 3)", "#f"},
		{"(>= 3 2)", "#t"},
		{"(>= 2 2)", "#t"},
		{"(>= 1 2)", "#f"},
		{"(> 2 1)", "#t"},
		{"(> 1 2)", "#f"},
		{"(> 3 2 1)", "#t"},
		{"(> 3 1 2)", "#f"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		var o obj.Object
		o, err = EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}

		if o.Type() != obj.BOOLEAN_OBJ {
			t.Errorf("eval object type want %v, got %v", obj.BOOLEAN_OBJ, o.Type())
		} else if o.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", o.Inspect(), tt.want)
		}
	}
}

func TestExpression002(t *testing.T) {
	//input := "(define (xor x y) (or (and x (not y)) (and (not x) y)))"
	input := "(define (xor x y) (or (and x (not y)) (and (not x) y)))"
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	lexer := lex.NewLexer(input)
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}
	obj, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("Eval() error = %v", err)
	}
	want := ""
	if obj.Inspect() != want {
		t.Errorf("Eval() = %v, want %v", obj.Inspect(), want)
	}
}

func TestProgram001(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{"#t", []string{"#t"}},
		{"#t #f", []string{"#t", "#f"}},
		{"(define x #t) x", []string{"", "#t"}},
		{"(define x (and #t #t)) x", []string{"", "#t"}},
		{"(define x (and #t #t)) (not x)", []string{"", "#f"}},
		{"(define (f x) x) (f #f)", []string{"", "#f"}},
		{"(define (xor x y) (or (and x (not y)) (and (not x) y))) (xor #t #f) (xor #f #f) (xor #t #t)", []string{"", "#t", "#f", "#f"}},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			if obj.Inspect() != tt.want[i] {
				t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want[i])
			}
		}
	}
}

func TestLet(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(let ((x #t)) x)", "#t"},
		{"(let ((x #f) (y #t)) (and x y))", "#f"},
		{"(let ((x #t) (y #t)) (and x y))", "#t"},
		{"(let ((x #t)) (let ((y #f)) (or x y)))", "#t"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}
		obj, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestExpression003(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		// Integer operations
		{"(+ 1 1)", "2"},
		{"(- 1 10)", "-9"},
		{"(* 2 3)", "6"},
		{"(/ 6 2)", "3"},
		{"(+ 10 20 30)", "60"},
		{"(- 100 30 20)", "50"},
		{"(* 2 3 4)", "24"},
		{"(/ 24 2 3)", "4"},
		{"(+ 0 5)", "5"},
		{"(- 5 0)", "5"},
		{"(* 7 0)", "0"},
		{"(/ 0 7)", "0"},
		{"(+ -5 -3)", "-8"},
		{"(- -5 -3)", "-2"}, // -5 - (-3) = -5 + 3 = -2
		{"(* -2 -3)", "6"},
		{"(/ -6 -3)", "2"},
		{"(* -2 3)", "-6"},
		{"(/ -6 3)", "-2"},

		// Rational operations
		{"(+ 1/2 1/2)", "1"},
		{"(- 3/4 1/4)", "1/2"},
		{"(* 2/3 3/4)", "1/2"},
		{"(/ 1/2 1/4)", "2"},
		{"(+ 1/3 1/6)", "1/2"},
		{"(- 1 1/2)", "1/2"},
		{"(* 3 2/5)", "6/5"},
		{"(/ 6 2/5)", "15"},
		{"(+ 1/2 1/3 1/6)", "1"},
		{"(- 5/6 1/3)", "1/2"},
		{"(* 1/2 2/3 3/4)", "1/4"},
		{"(/ 1 2 3 4)", "1/24"},

		// Real operations
		{"(+ 1.5 2.5)", "4.0"},
		{"(- 5.5 2.2)", "3.3"},
		{"(* 2.5 4.0)", "10.0"},
		{"(/ 7.5 2.5)", "3.0"},

		// Complex operations
		{"(+ 1+2i 2+3i)", "3+5i"},
		{"(- 5+3i 2+1i)", "3+2i"},
		{"(* 2+3i 1+1i)", "-1+5i"},
		{"(/ 4+2i 1+1i)", "3-1i"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}
		obj, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestIfExpressionIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{
			"(if #t 1 2)",
			[]string{"1"},
		},
		{
			"(if #f 1 2)",
			[]string{"2"},
		},
		{
			"(if #t 1)",
			[]string{"1"},
		},
		{
			"(if #f 1)",
			[]string{""}, // null value
		},
		{
			"(if (> 2 1) 10 20) (if (< 2 1) 10 20)",
			[]string{"10", "20"},
		},
		{
			"(define x 5) (if (> x 3) 1 0)",
			[]string{"", "1"},
		},
		{
			"(define (abs x) (if (< x 0) (- x) x)) (abs -5) (abs 3)",
			[]string{"", "5", "3"},
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			if obj.Inspect() != tt.want[i] {
				t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want[i])
			}
		}
	}
}

func TestQuoteExpressionIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{
			"'less",
			[]string{"less"},
		},
		{
			"'abc 6 5",
			[]string{"abc", "6", "5"},
		},
		{
			"(quote abc) 6 5",
			[]string{"abc", "6", "5"},
		},
		{
			"(quote greater)",
			[]string{"greater"},
		},
		{
			"(define x 'greater)",
			[]string{""},
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			if obj.Inspect() != tt.want[i] {
				t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want[i])
			}
		}
	}
}
